package de.klenkse.eclipse.ftpsync.model;

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import de.klenkse.eclipse.ftpsync.FTPSyncSetupException;

/**
 * Utilities for parsing and saving the model. Probably should be reworked...
 */
public abstract class FTPSyncModelUtils {
	/**
	 * Key of config.
	 */
	private final static String CONFIG = "config";
	/**
	 * Key of resource.
	 */
	private final static String RESOURCE = "resource";
	/**
	 * Key of entry.
	 */
	private final static String ENTRY = "entry";
	/**
	 * Key of server.
	 */
	private final static String SERVER = "server";
	/**
	 * Key of username.
	 */
	private final static String USERNAME = "username";
	/**
	 * Key of password.
	 */
	private final static String PASSWORD = "password";
	/**
	 * Key of base.
	 */
	private final static String BASE = "base";

	/**
	 * Handler for SAX parser.
	 * 
	 * @author Froschi
	 * @author $Author: hummelb $
	 * @version $Rev: 18709 $
	 */
	private static class FTPConfigContentHandler extends DefaultHandler {
		/**
		 * The model.
		 */
		private FTPConfigModel model;

		/**
		 * The entry.
		 */
		private FTPConfigEntry currentEntry = null;

		/**
		 * The current element.
		 */
		private String currentElement = null;

		/**
		 * The constructor.
		 */
		public FTPConfigContentHandler(FTPConfigModel model) {
			this.model = model;
		}

		/** {@inheritDoc} */
		@Override
		public void startElement(String uri, String name, String qName,
				Attributes attributes) {
			if (name.equals(ENTRY)) {
				currentEntry = new FTPConfigEntry();
			} else if (name.equals(RESOURCE)) {
				currentElement = RESOURCE;
			} else if (name.equals(SERVER)) {
				currentElement = SERVER;
			} else if (name.equals(BASE)) {
				currentElement = BASE;
			} else if (name.equals(USERNAME)) {
				currentElement = USERNAME;
			} else if (name.equals(PASSWORD)) {
				currentElement = PASSWORD;
			}
		}

		/** {@inheritDoc} */
		@Override
		public void endElement(String uri, String localName, String qName) {
			if (currentEntry == null) {
				return;
			}
			if (localName.equals(ENTRY)) {
				model.addConfigEntry(currentEntry);
			}
			currentElement = null;
		}

		/** {@inheritDoc} */
		@Override
		public void characters(char[] ch, int start, int length) {
			String elem = "";
			for (int i = start; i < start + length; i++) {
				elem += ch[i];
			}
			if (currentElement == null) {
				return;
			} else if (currentElement.equals(RESOURCE)) {
				currentEntry.setSyncBase(ResourcesPlugin.getWorkspace()
						.getRoot().findMember(elem));
			} else if (currentElement.equals(SERVER)) {
				currentEntry.setServerName(elem);
			} else if (currentElement.equals(BASE)) {
				currentEntry.setBasePath(elem);
			} else if (currentElement.equals(USERNAME)) {
				currentEntry.setUserName(elem);
			} else if (currentElement.equals(PASSWORD)) {
				currentEntry.setPassword(elem);
			}

		}
	}

	/**
	 * Save the model to the file given.
	 */
	public static void saveModel(FTPConfigModel model, IFile file) {
		try {

			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder;
			docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			Element root = doc.createElement(CONFIG);
			doc.appendChild(root);
			for (FTPConfigEntry entry : model.getConfigEntries()) {
				Element ent = doc.createElement(ENTRY);
				root.appendChild(ent);
				Element elem = doc.createElement(BASE);
				ent.appendChild(elem);
				Text text = doc.createTextNode(entry.getBasePath());
				elem.appendChild(text);
				elem = doc.createElement(USERNAME);
				ent.appendChild(elem);
				text = doc.createTextNode(entry.getUserName());
				elem.appendChild(text);
				elem = doc.createElement(PASSWORD);
				ent.appendChild(elem);
				text = doc.createTextNode(entry.getPassword());
				elem.appendChild(text);
				elem = doc.createElement(SERVER);
				ent.appendChild(elem);
				text = doc.createTextNode(entry.getServerName());
				elem.appendChild(text);
				elem = doc.createElement(RESOURCE);
				ent.appendChild(elem);
				text = doc.createTextNode(entry.getSyncBase().toString());
				elem.appendChild(text);
			}
			// set up a transformer
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			// create string from xml tree
			PipedInputStream is = new PipedInputStream();
			PipedOutputStream os = new PipedOutputStream(is);
			StreamResult result = new StreamResult(os);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			file.setContents(is, false, true, null);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open a model.
	 * 
	 * @throws FTPSyncSetupException
	 */
	public static FTPConfigModel openModel(InputStream input)
			throws FTPSyncSetupException {
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			FTPConfigModel model = new FTPConfigModel();
			FTPConfigContentHandler handler = new FTPConfigContentHandler(model);
			reader.setContentHandler(handler);
			reader.setErrorHandler(handler);
			reader.parse(new InputSource(input));
			return model;
		} catch (Exception e) {
			throw new FTPSyncSetupException(e);
		}
	}

	/**
	 * Open a model.
	 * 
	 * @throws FTPSyncSetupException
	 */
	public static FTPConfigModel openModel(IFile file)
			throws FTPSyncSetupException {
		try {
			return openModel(file.getContents());
		} catch (Exception e) {
			throw new FTPSyncSetupException(e);
		}
	}
}
